/**
 * 
 */
package prj3;

import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Iterator;

import prj3.ui.EarthDisplay;
/**
 * @author romain
 *
 */
public abstract class SphericalGrid implements IGrid, Runnable {


    public static final int STOPPED = 0;
    public static final int RUNNING = 1;
    public static final int PAUSED = 2;
    
    protected GregorianCalendar simulatedDateTime;
    
    
	/**
	 * Construct a grid with equal angular spacing 
	 * in PHI and OMEGA angles.
	 * @param radius is the radius of the sphere (in meters)
	 * @param step is the angular spacing (1-180) (in radians)
	 */
	public SphericalGrid(float radius, float step) {
		this(radius, step, step);
	}
	
	/**
	 * Construct a grid with different omega and 
	 * phi angular spacings
	 * @param _radius is the radius of the sphere (in meters)
	 * @param _stepTheta is the angular spacing (0-PI) (in radians)
	 * @param _stepPhi is the angular spacing (0-PI) (in radians)
	 */
	public SphericalGrid(float _radius, float _stepTheta, float _stepPhi) {
		if(_radius <= 0 || _stepTheta <= 0 || _stepTheta >= Math.PI || _stepPhi <= 0 || _stepPhi >= Math.PI)
			throw new IllegalArgumentException("Grid parameters are invalid");
		
		radius = _radius;
		stepTheta = _stepTheta;
		stepPhi = _stepPhi;
		
		/* Now we create the cells with the given spacing */
		cellMap = new HashMap();
        simulatedDateTime = (GregorianCalendar)util.Constants.startDate.clone();
        
		maxLatitude = (int)Math.round((Math.PI/stepTheta));
		maxLongitude = (int)Math.round((Math.PI*2/stepPhi));
		System.out.println("MaximumLatitude: " + maxLatitude + " and MaximumLongitude: " + maxLongitude);
		for(int longi=0; longi < maxLongitude; longi++) {
			for(int lati=1; lati <= maxLatitude; lati++) {
				SphericalCoordinate tC = new SphericalCoordinate(this, radius, longi, lati);
				cellMap.put(tC, new SphericalCell(this, tC));
			}
		}	
	}
	
    public void run(){
        runState = RUNNING;
        while(runState != STOPPED && maxIterations != 0) {
        	while(runState == RUNNING){
        		this.stepGrid(stepSize);
        		maxIterations--;
        		Thread.yield();
        	}
        	try {
        		Thread.sleep(10);
        	} catch(InterruptedException ex) {}
        }
    }
    
	/**
	 * Gets the PHI angle separation between two longitudes as measured
	 * along the equator
	 * @return float separation (in radians)
	 */
	public float getStepPhi() {
		return stepPhi;
	}
	
	/**
	 * Gets the OMEGA angle separation between two lattitudes
	 * @return float separation (in radians)
	 */
	public float getStepTheta() {
		return stepTheta;
	}
	
	/**
	 * Returns the radius of the sphere
	 * @return float
	 */
	public float getRadius() {
		return radius;
	}
	
	public SphericalCell getCellForCoordinate(SphericalCoordinate coord) {
		if(coord == null)
			return null;
		return (SphericalCell)cellMap.get(coord);
	}

    public java.util.Collection getAllCells() {
	return cellMap.values();
    }
	
	/**
	 * Gets the maximum longitude value. Note that
	 * this is circular so if this returns 12, it means that
	 * longitude 12 = longitude 0
	 * @return
	 */
	public int getMaximumLongitude() {
		return maxLongitude;
	}
	
	/**
	 * Gets the maximum latitude (latitude 0 is north pole
	 * and the value returned is the south pole)
	 * @return
	 */
	public int getMaximumLatitude() {
		return maxLatitude;
	}
	
	/* Private data */

    protected EarthDisplay display = null;
	protected float stepPhi, stepTheta, radius; // Grid definition
	protected HashMap cellMap;
	protected int maxLongitude, maxLatitude;
    protected int stepSize = 1;
    protected long maxIterations = 0;
    protected int runState = STOPPED;
	
	/* Inherited methods */
	/* (non-Javadoc)
	 * @see prj3.IGrid#getFloatValue(prj3.ICoordinate)
	 */
	public float getFloatValue(ICoordinate coord) {
		SphericalCoordinate tC = (SphericalCoordinate)coord;
		SphericalCell cell = (SphericalCell)cellMap.get(tC);
		if(cell != null)
			return cell.getValue();
		return 0;
	}

	/* (non-Javadoc)
	 * @see prj3.IGrid#getNeighbor(prj3.ICoordinate, int)
	 */
	public ICoordinate getNeighbor(ICoordinate coord, int direction) {
		SphericalCoordinate myCoord = (SphericalCoordinate)coord;
		SphericalCoordinate newCoord;
		switch(direction) {
		case ICoordinate.DIR_BOTTOM:
			if(myCoord.getLatitude() < maxLatitude)
				newCoord = new SphericalCoordinate(this, myCoord.getRadius(), 
					myCoord.getLongitude(), myCoord.getLatitude() + 1);
			else
				/* There is no bottom */
				newCoord = null;
			break;
		case ICoordinate.DIR_TOP:
			if(myCoord.getLatitude() > 1)
				newCoord = new SphericalCoordinate(this, myCoord.getRadius(), 
						myCoord.getLongitude(), myCoord.getLatitude() - 1);
			else
				/* There is no top */
				newCoord = null;
			break;
		case ICoordinate.DIR_LEFT:
			if(myCoord.getLongitude() == 0)
				newCoord = new SphericalCoordinate(this, myCoord.getRadius(), 
						maxLongitude - 1, myCoord.getLatitude());
			else
				newCoord = new SphericalCoordinate(this, myCoord.getRadius(), 
						myCoord.getLongitude() - 1, myCoord.getLatitude());
			break;
		case ICoordinate.DIR_RIGHT:
			if(myCoord.getLongitude() < maxLongitude - 1)
				newCoord = new SphericalCoordinate(this, myCoord.getRadius(), 
						myCoord.getLongitude() + 1, myCoord.getLatitude());
			else
				newCoord = new SphericalCoordinate(this, myCoord.getRadius(), 
						0, myCoord.getLatitude());
			break;
		default:
			newCoord = null;
		}
		return newCoord;
	}

	/* (non-Javadoc)
	 * @see prj3.IGrid#isDone()
	 */
	public boolean isDone() {
		return false; /* There is no stopping here */
	}

	/* (non-Javadoc)
	 * @see prj3.IGrid#printCell(prj3.ICoordinate)
	 */
	public String printCell(ICoordinate coord) {
		SphericalCoordinate tC = (SphericalCoordinate)coord;
		SphericalCell cell = (SphericalCell)cellMap.get(tC);
		if(cell != null) {
			return new String("(" + new Float(tC.getRadius()).toString() + ", " +
					new Float(tC.getTheta()).toString() + ", " +
					new Float(tC.getPhi()).toString() + ") = " +
					new Float(cell.getValue()).toString());
		}
		return null;
	}
    
    public String printCellLatLong(ICoordinate coord) {
        SphericalCoordinate tC = (SphericalCoordinate)coord;
        SphericalCell cell = (SphericalCell)cellMap.get(tC);
        if(cell != null) {
            return new String("(" + new Float(tC.getRadius()).toString() + ", " +
                    new Float(tC.getLatitude()).toString() + ", " +
                    new Float(tC.getLongitude()).toString() + ") = " +
                    new Float(cell.getValue()).toString());
        }
        return null;
    }

	/* (non-Javadoc)
	 * @see prj3.IGrid#setFloatValue(prj3.ICoordinate, float)
	 */
	public void setFloatValue(ICoordinate coord, float value) {
		SphericalCoordinate tC = (SphericalCoordinate)coord;
		SphericalCell cell = (SphericalCell)cellMap.get(tC);
		if(cell != null)
			if(cell.isMutable())
				cell.setValue(value);
			else {
				cell.setMutable(true);
				cell.setValue(value);
				cell.setMutable(false);
			}
	}

	/* (non-Javadoc)
	 * @see prj3.IGrid#stepGrid()
	 */
	public void stepGrid() {
		return; 
		/* We should do something in this case for 
		 * a normal spherical grid but this would be extra
		 * code which we would not use for our program.
		 * We will thus not write this method. It will be over-ridden by
		 * EarthGrid
		 */

	}
    
    /* (non-Javadoc)
     * @see prj3.IGrid#stepGrid()
     */
    public abstract void stepGrid(int i);
        
    
    public Iterator iterator(){
       return new SphericalGridIterator(this); 
    }
	
    public void setDisplay(EarthDisplay display){
        this.display = display;
    }

    public int getStepSize() {
        return stepSize;
    }

    public void setStepSize(int stepSize) {
        this.stepSize = stepSize;
    }
    
    public void setMaxIterations(long iterations) {
        this.maxIterations = iterations;
    }
    
    public int getRunState() {
        return runState;
    }

    public void setRunState(int runState) {
        this.runState = runState;
    }

    

   

}
